/**
 * @file UDPRetroreflectionClient.c
 * @author haiming
 * @brief UDP单播客户端@dg_s_cli、广播客户端@dg_b_cli
 * @note 广播地址并不一定是(子网ID.255)，可以通过查看网络详情确定古广播地址(ifconfig)
 * @version 0.1
 * @date 2022-08-19
 * 
 * @copyright Copyright (c) 2022
 * 
 */
#include "NetWorkProDefine.h"
#include "SocketTimeOut.h"

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>


static void recvfrom_alarm(int signo);
void dg_b_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen);
void dg_s_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen);
void dg_m_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen);


int main(int argc, char ** argv)
{
    printf("welcome to UDP client\n");
    int sockfd;
    struct sockaddr_in servaddr;

    if(argc != 3)
    {
        printf("usage: %s <IP> <type s/b/m>\n", *argv);
        return -1;
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    if ('s' == *argv[2])
    {
        dg_s_cli(stdin, sockfd, (SA*)& servaddr, sizeof(servaddr));
    }
    else if('b' == *argv[2])
    {
        dg_b_cli(stdin, sockfd, (SA*)& servaddr, sizeof(servaddr));
    }
    else if('m' == *argv[2])
    {
        dg_m_cli(stdin, sockfd, (SA*)& servaddr, sizeof(servaddr));
    }
    return 0;
}



void dg_s_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen)
{
    printf("dg_s_cli\n");
    char sendline[MAXLEN], recvline[MAXLEN];

    while (fgets(sendline, MAXLEN, fp) != NULL)
    {
        size_t sendlen = sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
        if (sendlen != strlen(sendline))
        {
            printf("sendto error, code :%u\tmsg :%s\n", errno, strerror(errno));
            break;
        }

        int readable;
        if((readable = readable_timeo(sockfd, 3)) > 0)
        {
            int n = recvfrom(sockfd, recvline, MAXLEN, 0, NULL, NULL);
            printf("server :%s\n",recvline);
            memset(sendline, 0, sizeof(sendline));
            memset(recvline, 0, sizeof(recvline));
        }
        else if(readable < 0)
        {
            printf("recvfrom error, code :%u\tmsg :%s\n", errno, strerror(errno));
            break;
        }
        else
        {
            printf("recvfrom timeout!\n");
            break;
        }
    }
}


static void recvfrom_alarm(int signo)
{
    printf("recvfrom_alarm\n");
    return ;
}


void dg_b_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen)
{
    printf("dg_b_cli\n");
    updatesigaction(recvfrom_alarm);
    const int on = 1;
    char sendline[MAXLEN], recvline[MAXLEN + 1];
    socklen_t len;
    struct sockaddr *preply_addr;

    preply_addr = (struct sockaddr*)malloc(servlen);
    setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));

    while (fgets(sendline, MAXLEN, fp) != NULL)
    {
        sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
        for (; ; )
        {
            alarm(3);
            len = servlen;
            int  n = recvfrom(sockfd, recvline, MAXLEN, 0, preply_addr, &len);
            if (n < 0 && errno == EINTR)
            {
                printf("recvfrom timeour.\n");
                alarm(0);
                break;
            }
            else if(n > 0)
            {
                recvline[n] = 0;
                display_sockaddr(preply_addr);
                alarm(0);
            }
            else
            {
                printf("recvfrom error, code :%u\tmsg :%s\n", errno, strerror(errno));
                free(preply_addr);
                exit(1);
            }
        }
    }
    free(preply_addr);
}


void dg_m_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen)
{
    printf("dg_m_cli\n");
    updatesigaction(recvfrom_alarm);
    const int on = 1;
    char sendline[MAXLEN], recvline[MAXLEN + 1];
    socklen_t len;
    struct sockaddr *preply_addr;

    preply_addr = (struct sockaddr*)malloc(servlen);

    while (fgets(sendline, MAXLEN, fp) != NULL)
    {
        sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
        for (; ; )
        {
            alarm(3);
            len = servlen;
            int  n = recvfrom(sockfd, recvline, MAXLEN, 0, preply_addr, &len);
            if (n < 0 && errno == EINTR)
            {
                printf("recvfrom timeour.\n");
                alarm(0);
                break;
            }
            else if(n > 0)
            {
                recvline[n] = 0;
                display_sockaddr(preply_addr);
                alarm(0);
                break;  //只接收一个服务端的数据
            }
            else
            {
                printf("recvfrom error, code :%u\tmsg :%s\n", errno, strerror(errno));
                free(preply_addr);
                exit(1);
            }
        }
    }
    free(preply_addr);
}